package org.example;

import org.example.enums.OperEnum;
import org.example.listener.RedoEventListener;
import org.example.listener.UndoEventListener;
import org.example.operator.*;
import org.example.redo.MemoryRedoLog;
import org.example.redo.Redo;
import org.example.undo.MemoryUndoLog;
import org.example.undo.Undo;
import com.google.common.eventbus.EventBus;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Objects;

/**
 *
 * 计算器类的实现，主要提供了加减乘除的方法，以及获取 undo redo 的操作记录
 * @author Silver
 * @return
 */
public class Calculator {

    /**
     * undo 操作类
     */
    private Undo undo;

    /**
     * redo 操作类
     */
    private Redo redo;

    /**
     * 加法
     */
    private Add add;

    /**
     * 减法
     */
    private Subtract subtract;

    /**
     * 乘法
     */
    private Multiply multiply;

    /**
     * 除法
     */
    private Divide divide;

    /**
     * 事件处理
     */
    private EventBus eventBus;

    /**
     * 用于记录计算后的结果
     */
    private ResultSource<BigDecimal> finalResult;

    /**
     * 用来表示 undo，redo 指针的位置。默认位置为-1
     * @author Silver
     */
    private int flags = -1;

    /**
     * 计算器的默认实现
     * @author Silver
     */
    public Calculator() throws Exception {
        //初始化默认实现
        this.undo = new MemoryUndoLog();
//        this.undo = new MemoryUndoObjLog();
//        this.undo = new FileUndoLog();
        this.redo = new MemoryRedoLog();
        this.eventBus = new EventBus("数据计算同步器");
        this.finalResult = new ResultSource();

        //注册事件监听
        registerEventListener();
        //加减乘除实现
        this.add = (Add) OperationBuilder.buildComplexNumberOperator(undo, redo, eventBus, finalResult, OperEnum.ADD);
        this.subtract = (Subtract) OperationBuilder.buildComplexNumberOperator(undo, redo, eventBus, finalResult, OperEnum.SUBTRACT);
        this.multiply = (Multiply) OperationBuilder.buildComplexNumberOperator(undo, redo, eventBus, finalResult, OperEnum.MULT);
        this.divide = (Divide) OperationBuilder.buildComplexNumberOperator(undo, redo, eventBus, finalResult, OperEnum.DIVIDE);
    }

    /**
     *
     * main 方法，测试实现使用
     * @author Silver
     * @param args
     * @return void
     */


    /**
     * 加法运算
     *
     * @author Silver
     * @param num1
     * @param num2
     * @return java.math.BigDecimal 返回值
     */
    public Calculator add(BigDecimal num1, BigDecimal num2) throws Exception {
        return operator(num1, num2, add);
    }

    /**
     *
     * 加法运算，用现有结果进行运算
     * @author Silver
     * @param num 用现有结果 和num进行相加
     * @return org.example.Calculator
     */
    public Calculator add(BigDecimal num) throws Exception {
        return operator(finalResult.getFinalResult(), num, add);
    }

    /**
     * 减法运算
     * @author Silver
     * @param num1
     * @param num2
     * @return java.math.BigDecimal 返回值
     */
    public Calculator subtract(BigDecimal num1, BigDecimal num2) throws Exception {
        return operator(num1, num2, subtract);
    }
    /**
     * 减法运算
     * @author Silver
     * @param num
     * @return java.math.BigDecimal 返回值
     */
    public Calculator subtract(BigDecimal num) throws Exception {
        return operator(finalResult.getFinalResult(), num, subtract);
    }

    /**
     *
     * 操作方法，用来处理两个数据的操作
     * @author Silver
     * @param num1 数据1
     * @param num2 数据2
     * @param complexNumberOperator 具体操作，传什么实现就是什么操作
     * @return org.example.Calculator 返回对象本身，方便可以进行链式操作
     */
    private Calculator operator(BigDecimal num1, BigDecimal num2, ComplexNumberOperator complexNumberOperator) throws Exception {
        checkNum(num1, num2);
        complexNumberOperator.setNum1(num1);
        complexNumberOperator.setNum2(num2);
        complexNumberOperator.operator();
        //每次操作之后指针重新赋值;表示最终位置,该部分可以继续优化，但是整体结构不需要改动了
        flags = redo.redo().size();
        return this;
    }

    /**
     * 乘法运算
     *
     * @author Silver
     * @param num1
     * @param num2
     * @return java.math.BigDecimal 返回值
     */
    public Calculator multiply(BigDecimal num1, BigDecimal num2) throws Exception {
        return operator(num1, num2, subtract);
    }

    public Calculator multiply(BigDecimal num) throws Exception {
        return operator(finalResult.getFinalResult(), num, multiply);
    }

    /**
     * 除法运算
     *
     * @author Silver
     * @param num1 除数
     * @param num2 被除数
     * @param roundingMode 四舍五入规则
     * @return java.math.BigDecimal 返回值
     */
    public Calculator divide(BigDecimal num1, BigDecimal num2,  RoundingMode roundingMode) throws Exception {
        divide.setRoundingMode(roundingMode);
        return operator(num1, num2, divide);
    }

    public Calculator divide(BigDecimal num, RoundingMode roundingMode) throws Exception {
        divide.setRoundingMode(roundingMode);
        return operator(finalResult.getFinalResult(), num, divide);
    }
    /**
     *
     * 校验数据合法性
     * @author Silver
     * @param num1
     * @param num2
     * @return void
     */
    public void checkNum(BigDecimal num1, BigDecimal num2) throws Exception {
        if (Objects.isNull(num1) || Objects.isNull(num2)){
            throw new Exception("数据不能为空");
        }
    }

    /**
     * redo操作
     * @author Silver
     * @param i redo的步数
     * @return java.math.BigDecimal 返回redo后具体的数据
     */
    public BigDecimal redo(int i){
        List<BigDecimal> redo1 = redo.redo();
        if (flags + i > redo1.size()){
            flags = redo1.size()-1;
            finalResult.setFinalResult(redo1.get(flags));
        } else if (flags + i >= 0 && flags + i < redo1.size()) {
            finalResult.setFinalResult(redo1.get(flags + i));
            flags += i;
            if (flags > redo1.size()){
                flags = redo1.size()-1;
            }
        }
        return finalResult.getFinalResult();
    }

    /**
     * undo操作
     * @author Silver
     * @param i undo的步数
     * @return java.math.BigDecimal 返回undo后具体的数据
     */
    public BigDecimal undo(int i){
        List<BigDecimal> undo1 = undo.undo();
        if (flags - (i-1) < 0){
            flags = -1;
            finalResult.setFinalResult(undo1.get(0));
        } else if (flags - (i-1) >= 0 && flags < undo1.size()) {
            finalResult.setFinalResult(undo1.get(flags - (i-1)));
            flags -= i;
            if (flags < 0){
                flags = -1;
            }
        }
        return finalResult.getFinalResult();
    }

    /**
     * 操作全流程redo
     * @author Silver
     * @return
     */
    public void redoList(){
        List<BigDecimal> redo1 = redo.redo();
        System.out.println("------------redo 遍历-------------");
        System.out.println(redo1);

    }

    public void undoList(){
        List<BigDecimal> undo1 = undo.undo();
        System.out.println("------------undo 遍历-------------");
        System.out.println(undo1);
    }

    /**
     * 注册事件监听
     * @author Silver
     * @param
     * @return void
     */
    public void registerEventListener() {
        eventBus.register(new UndoEventListener("undo 监听器", undo));
        //可以实现扩展的 undo 实现
//        eventBus.register(new UndoEventDologObjListener("undo 监听器", undo));
        eventBus.register(new RedoEventListener("redo 监听器", redo));
    }

    public int getFlags() {
        return flags;
    }

    public ResultSource<BigDecimal> getFinalResult() {
        return finalResult;
    }
}
